#include "QQStorage.h"

/*
QQTimerThread::QQTimerThread(QObject* parent)
	: QThread(parent)
{
	mTimer = nullptr;
}

void QQTimerThread::setTimerInterval(int interval)
{
	mTimer->setInterval(interval);
}

void QQTimerThread::setTimeOutFunction(const std::function<void()>& func)
{
	mFunc = func;
}

void QQTimerThread::run()
{
	mTimer = new QTimer(this);
	mTimer->setSingleShot(true);
	mTimer->moveToThread(this);
	connect(mTimer, &QTimer::timeout, mFunc);
	mTimer->start();
	this->exec();
}

QQThreadPool::QQThreadPool(int threadCount, QObject* parent)
	: QObject(parent)
{
	mTimeOut = 3000;
	mThreadCount = threadCount;
	QThreadPool::globalInstance()->setMaxThreadCount(mThreadCount);
}

QQThreadPool::~QQThreadPool()
{
	waitAllWorkerFinished();
	qDeleteAll(mFutureWatchers);
}

void QQThreadPool::setWorkerTimeout(int timeOut)
{
	mTimeOut = timeOut;
}

int QQThreadPool::getWorkerTimeout() const
{
	return mTimeOut;
}

void QQThreadPool::setWorkerThreadCount(int threadCount)
{
	if (mThreadCount == threadCount || threadCount < QQSystemApi::getCpuCoreCount())
	{
		return;
	}
	waitAllWorkerFinished();
	mThreadCount = threadCount;
	QThreadPool::globalInstance()->setMaxThreadCount(mThreadCount);
}

int QQThreadPool::getWorkerThreadCount() const
{
	return mThreadCount;
}

template<typename Worker, typename ...Args>
QFuture<QQThreadPool::FinishType> QQThreadPool::submit(Worker&& worker, Args && ...args)
{
	QFutureInterface<QQThreadPool::FinishType> futureInterface;
	auto task = [&futureInterface, worker = std::forward<Worker>(worker),
		args = std::make_tuple(std::forward<Args>(args)...)]()mutable
		{
			std::apply(worker, std::move(args));
			if (!futureInterface.isFinished())
			{
				futureInterface.reportResult(QQThreadPool::FinishType::Success);
				futureInterface.reportFinished();
			}
		};
	QQTimerThread* timerThread = new QQTimerThread(this);
	mTimerThreads.insert(futureInterface.future(), timerThread);
	timerThread->setTimerInterval(mTimeOut);
	timerThread->setTimeOutFunction([&futureInterface, timerThread]() {
		timerThread->quit();
		if (!futureInterface.isFinished())
		{
			futureInterface.reportResult(QQThreadPool::FinishType::TimeOut);
			futureInterface.reportFinished();
		}
		});
	QFutureWatcher<QQThreadPool::FinishType>* watcher = new QFutureWatcher<QQThreadPool::FinishType>(this);
	watcher->setFuture(futureInterface.future());
	connect(watcher, &QFutureWatcher<QQThreadPool::FinishType>::finished, this, &QQThreadPool::onFutureFinished);
	mFutureWatchers.append(watcher);
	futureInterface.reportStarted();
	timerThread->start();
	QThreadPool::globalInstance()->start(task);
	return futureInterface.future();
}

template<typename Worker, typename Return, typename ...Args>
auto QQThreadPool::submitNeedReturn(Worker&& worker, Return&& value, Args && ...args)-> typename std::enable_if<std::is_reference<Return>::value, QFuture<QQThreadPool::FinishType>>::type
{
	static_assert(std::is_reference<Return>::value, "Return type must be a reference.");
	QFutureInterface<QQThreadPool::FinishType> futureInterface;
	auto task = [&futureInterface, &value, worker = std::forward<Worker>(worker),
		args = std::make_tuple(std::forward<Args>(args)...)]()mutable
		{
			value = std::apply(worker, std::move(args));
			if (!futureInterface.isFinished())
			{
				futureInterface.reportResult(QQThreadPool::FinishType::Success);
				futureInterface.reportFinished();
			}
		};
	QQTimerThread* timerThread = new QQTimerThread(this);
	mTimerThreads.insert(futureInterface.future(), timerThread);
	timerThread->setTimerInterval(mTimeOut);
	timerThread->setTimeOutFunction([&futureInterface, timerThread]() {
		timerThread->quit();
		if (!futureInterface.isFinished())
		{
			futureInterface.reportResult(QQThreadPool::FinishType::TimeOut);
			futureInterface.reportFinished();
		}
		});
	QFutureWatcher<QQThreadPool::FinishType>* watcher = new QFutureWatcher<QQThreadPool::FinishType>(this);
	watcher->setFuture(futureInterface.future());
	connect(watcher, &QFutureWatcher<QQThreadPool::FinishType>::finished, this, &QQThreadPool::onFutureFinished);
	mFutureWatchers.append(watcher);
	futureInterface.reportStarted();
	timerThread->start();
	QThreadPool::globalInstance()->start(task);
	return futureInterface.future();
}

void QQThreadPool::onFutureFinished()
{
	QMutexLocker locker(&mMutex);
	QFutureWatcher<QQThreadPool::FinishType>* watcher = static_cast<QFutureWatcher<QQThreadPool::FinishType>*>(sender());
	if (watcher)
	{
		QFuture<QQThreadPool::FinishType> future = watcher->future();
		if (mTimerThreads.contains(future))
		{
			QQTimerThread* timerThread = mTimerThreads.take(future);
			timerThread->quit();
			timerThread->wait();
			timerThread->deleteLater();
		}
		mFutureWatchers.removeAt(mFutureWatchers.indexOf(watcher));
		watcher->deleteLater();
	}
}

void QQThreadPool::waitAllWorkerFinished()
{
	QMutexLocker locker(&mMutex);
	for (auto watcher : mFutureWatchers) {
		watcher->waitForFinished();
	}
}

QQStorageReadThread::QQStorageReadThread(QObject* parent)
	: QThread(parent)
{
}

QQStorageReadThread::~QQStorageReadThread()
{
}

void QQStorageReadThread::submit(const std::function<void()>& work)
{
	mRead = work;
	if (!this->isRunning())
		this->start();
}

void QQStorageReadThread::stop()
{
	if (this->isRunning())
		this->wait();
}

void QQStorageReadThread::run()
{
	if (mRead)
	{
		mRead();
	}
}

QQStorageReadThreadPool::QQStorageReadThreadPool(int threadCount, QObject* parent)
	: QObject(parent)
{
	for (size_t i = 0; i < threadCount; ++i)
	{
		auto thread = new QQStorageReadThread(this);
		thread->submit([this]()
			{
				while (true)
				{
					QMutexLocker locker(&mMutex);
					while (mReadWorks.empty() && !mIsStop)
					{
						mWaitCondition.wait(&mMutex);
					}
					if (mIsStop && mReadWorks.empty())
					{
						return;
					}
					if (!mReadWorks.empty())
					{
						auto task = mReadWorks.dequeue();
						mReadWriteLock->lockForRead();
						try
						{
							task();
						}
						catch (const std::exception& e)
						{
							qWarning() << "Task execution failed:" << e.what();
						}
					}
				}
			});
		mReadThreads.append(thread);
	}
}

QQStorageReadThreadPool::~QQStorageReadThreadPool()
{
	mIsStop = true;
	mWaitCondition.wakeAll();
	for (auto thread : mReadThreads)
	{
		thread->stop();
	}
	qDeleteAll(mReadThreads);
	qDeleteAll(mReadWorks);
}

void QQStorageReadThreadPool::submit(const std::function<void()>& work)
{
	QMutexLocker locker(&mMutex);
	mReadWorks.append(work);
	mWaitCondition.wakeAll();
}

void QQStorageReadThreadPool::setReadWriteLock(QReadWriteLock* readWriteLock)
{
	mReadWriteLock = readWriteLock;
}

QQStorageWriteThread::QQStorageWriteThread(QObject* parent)
	: QThread(parent)
{
}

QQStorageWriteThread::~QQStorageWriteThread()
{
	if (this->isRunning())
		this->wait();
}

void QQStorageWriteThread::submit(const std::function<void()>& work)
{
	mWriteWorks.push(work);
	if (!this->isRunning())
		this->start();
}

void QQStorageWriteThread::setReadWriteLock(QReadWriteLock* readWriteLock)
{
	mReadWriteLock = readWriteLock;
}

void QQStorageWriteThread::stop()
{
	if (this->isRunning())
		this->wait();
}

void QQStorageWriteThread::run()
{
	while (!mIsStop)
	{
		if (mWriteWorks.empty())
			this->msleep(100);
		auto task = mWriteWorks.front();
		if (task)
		{
			mReadWriteLock->lockForWrite();
			task();
			mWriteWorks.pop();
		}
	}
}

QQStorage::QQStorage(QObject* parent)
	: QObject(parent)
{
	mReadWriteLock = new QReadWriteLock;
	mReadThreadPool = new QQStorageReadThreadPool(QThread::idealThreadCount(), this);
	mReadThreadPool->setReadWriteLock(mReadWriteLock);
	mWriteThread = new QQStorageWriteThread(this);
	mWriteThread->setReadWriteLock(mReadWriteLock);
}

QQStorage::~QQStorage()
{
}

QQStorage* QQStorage::getInstance()
{
	static QQStorage* instance = nullptr;
	if (instance == nullptr)
		instance = new QQStorage(qApp);
	return instance;
}

void QQStorage::init()
{

}
*/
